`include "defines.svh"
`include "CP0Defines.svh"

typedef enum logic[2:0] {
    MUL_IDLE,
    MUL_WAIT1,
    MUL_WAIT2,
    MUL_WAIT3,
    MUL_WAIT4,
    MUL_WAIT5,
    MUL_FINISH
} MultState;

typedef enum logic[1:0] {
    DIV_IDLE,
    DIV_REQ,
    DIV_WAIT
} DivState;

module ALU2(
    input logic clk,
    input logic reset,
    input logic [4:0]EXE_ALUOp,
    input logic [31:0]A,
    input logic [31:0]B,
    input ExceptionType WB_ExceptionType,
    output logic [31:0]EXE_DatatoLO,
    output logic [31:0]EXE_DatatoHI,
    output logic EXE_Finish,
    output logic EXE_ALU2_Stall
);
// div -->  dividend_tdata / divisor_tdata 
// 除号后面的叫做除数(divisor_tdata)
logic  [31:0]   divisor_tdata;      // 除数
logic  [31:0]   dividend_tdata;     // 被除数
logic  [32:0]   Result_A33;         
logic  [32:0]   Result_B33;
logic  [65:0]   mult_out;
logic           mult_finish;   
logic           div_finish;   

logic           Unsigned_divisor_tvalid;
logic           Unsigned_dividend_tvalid;
logic           Unsigned_divisor_tready;
logic           Unsigned_dividend_tready;

logic           Signed_divisor_tvalid;
logic           Signed_dividend_tvalid;
logic           Signed_divisor_tready;
logic           Signed_dividend_tready;

logic           Signed_div_finish;
logic           Unsigned_div_finish;

logic  [63:0]   Signed_dout_tdata;
logic  [63:0]   Unsigned_dout_tdata;

logic  [1:0]    nextstate;
logic  [1:0]    prestate;  

always_ff @(posedge clk or negedge reset) begin
    if (!reset) begin
        dividend_tdata <= 32'b0;
        divisor_tdata  <= 32'b0;
    end
    else if (prestate == DIV_IDLE && (EXE_ALUOp == `ALU_div || EXE_ALUOp == `ALU_divu)) begin
        dividend_tdata <= A;
        divisor_tdata  <= B;
    end
end

// always_comb begin
//     if(EXE_ALUsel == `ALU2)begin
//         case (EXE_ALUOp)
//             `ALU_mul, `ALU_mulu:begin
//                 if(EXE_ALUOp == `ALU_mul)begin
//                     Result_A33 = {A[31],A};
//                     Result_B33 = {B[31],B};
//                 end
//                 else begin
//                     Result_A33 = {1'b0,A};
//                     Result_B33 = {1'b0,B};
//                 end
//                 mult_out = $signed(Result_A33) * $signed(Result_B33);
//             end
//             default: mult_out = '0;//Do nothing
//         endcase
//     end
//     else mult_out = '0;
// end 

// 除法的状态机
always_ff @(posedge clk or negedge reset) begin
    if (!reset) prestate <= DIV_IDLE;
    else prestate <= nextstate;
end

always_comb begin
        if (prestate == DIV_IDLE) begin
            Signed_divisor_tvalid    = 1'b0;
            Signed_dividend_tvalid   = 1'b0;
            Unsigned_divisor_tvalid  = 1'b0;
            Unsigned_dividend_tvalid = 1'b0;
        end  
        else if (prestate == DIV_REQ) begin
            if (EXE_ALUOp == `ALU_div) begin
                Signed_divisor_tvalid    = 1'b1;
                Signed_dividend_tvalid   = 1'b1;
                Unsigned_divisor_tvalid  = 1'b0;
                Unsigned_dividend_tvalid = 1'b0;
            end 
            else if(EXE_ALUOp == `ALU_divu) begin
                Signed_divisor_tvalid    = 1'b0;
                Signed_dividend_tvalid   = 1'b0;
                Unsigned_divisor_tvalid  = 1'b1;
                Unsigned_dividend_tvalid = 1'b1;
            end
            else begin
                Signed_divisor_tvalid    = 1'b0;
                Signed_dividend_tvalid   = 1'b0;
                Unsigned_divisor_tvalid  = 1'b0;
                Unsigned_dividend_tvalid = 1'b0;
            end
        end
        else if (prestate == DIV_WAIT) begin
            Signed_divisor_tvalid      = 1'b0;
            Signed_dividend_tvalid     = 1'b0;
            Unsigned_divisor_tvalid    = 1'b0;
            Unsigned_dividend_tvalid   = 1'b0;
        end else begin
            Signed_divisor_tvalid      = 1'b0;
            Signed_dividend_tvalid     = 1'b0;
            Unsigned_divisor_tvalid    = 1'b0;
            Unsigned_dividend_tvalid   = 1'b0;
        end
    end
// 除法状�?�机的控制信�?
always_comb begin
        if(WB_ExceptionType!=`NoException)begin
            nextstate=DIV_IDLE;
        end
        else begin
            case (prestate) 
            DIV_IDLE: begin
                if (EXE_ALUOp == `ALU_div || EXE_ALUOp == `ALU_divu) begin
                    nextstate = DIV_REQ;
                end else begin
                    nextstate = DIV_IDLE;
                end
            end
            DIV_REQ: begin
                if (((Signed_dividend_tready == 1'b1 && Signed_divisor_tready == 1'b1) && EXE_ALUOp == `ALU_div ) ||
                    ((Unsigned_dividend_tready == 1'b1 && Unsigned_divisor_tready == 1'b1) && EXE_ALUOp == `ALU_divu )) begin
                    nextstate = DIV_WAIT;
                end else begin
                    nextstate = DIV_REQ;
                end
            end
            DIV_WAIT: begin
                if (div_finish == 1'b1) begin
                    nextstate = DIV_IDLE;
                end else begin
                    nextstate = DIV_WAIT;
                end
            end
            default: begin
                nextstate = DIV_IDLE;
            end
        endcase
        end  
    end

//若用modelsim�?,没IP核需要把下面两个模块注释�?
`ifdef USE_VERILATOR
    Signed_div_verilator U_SignedDIV (
        .aclk(clk),                                         // input wire aclk
        .aresetn(reset),
        .s_axis_divisor_tvalid (Signed_divisor_tvalid),      // input wire s_axis_divisor_tvalid
        .s_axis_divisor_tready (Signed_divisor_tready),      // output wire s_axis_divisor_tready
        .s_axis_divisor_tdata  (divisor_tdata),              // input wire [31 : 0] s_axis_divisor_tdata
        .s_axis_dividend_tvalid(Signed_dividend_tvalid),     // input wire s_axis_dividend_tvalid
        .s_axis_dividend_tready(Signed_dividend_tready),     // output wire s_axis_dividend_tready
        .s_axis_dividend_tdata (dividend_tdata),             // input wire [31 : 0] s_axis_dividend_tdata
        .m_axis_dout_tvalid    (Signed_div_finish),          // output wire m_axis_dout_tvalid
        .m_axis_dout_tdata     (Signed_dout_tdata)           // output wire [63 : 0] m_axis_dout_tdata
        );

    Unsigned_div_verilator U_UnsignedDIV (
    .aclk(clk),                                          // input wire aclk
    .aresetn(reset),
    .s_axis_divisor_tvalid  (Unsigned_divisor_tvalid),    // input wire s_axis_divisor_tvalid
    .s_axis_divisor_tready  (Unsigned_divisor_tready),    // output wire s_axis_divisor_tready
    .s_axis_divisor_tdata   (divisor_tdata),              // input wire [31 : 0] s_axis_divisor_tdata
    .s_axis_dividend_tvalid (Unsigned_dividend_tvalid),   // input wire s_axis_dividend_tvalid
    .s_axis_dividend_tready (Unsigned_dividend_tready),   // output wire s_axis_dividend_tready
    .s_axis_dividend_tdata  (dividend_tdata),             // input wire [31 : 0] s_axis_dividend_tdata
    .m_axis_dout_tvalid     (Unsigned_div_finish),        // output wire m_axis_dout_tvalid
    .m_axis_dout_tdata      (Unsigned_dout_tdata)         // output wire [63 : 0] m_axis_dout_tdata
    );
`else
    Signed_div U_SignedDIV (
        .aclk(clk),                                         // input wire aclk
        .s_axis_divisor_tvalid (Signed_divisor_tvalid),      // input wire s_axis_divisor_tvalid
        .s_axis_divisor_tready (Signed_divisor_tready),      // output wire s_axis_divisor_tready
        .s_axis_divisor_tdata  (divisor_tdata),              // input wire [31 : 0] s_axis_divisor_tdata
        .s_axis_dividend_tvalid(Signed_dividend_tvalid),     // input wire s_axis_dividend_tvalid
        .s_axis_dividend_tready(Signed_dividend_tready),     // output wire s_axis_dividend_tready
        .s_axis_dividend_tdata (dividend_tdata),             // input wire [31 : 0] s_axis_dividend_tdata
        .m_axis_dout_tvalid    (Signed_div_finish),          // output wire m_axis_dout_tvalid
        .m_axis_dout_tdata     (Signed_dout_tdata)           // output wire [63 : 0] m_axis_dout_tdata
        );

    Unsigned_div U_UnsignedDIV (
    .aclk(clk),                                          // input wire aclk
    .s_axis_divisor_tvalid  (Unsigned_divisor_tvalid),    // input wire s_axis_divisor_tvalid
    .s_axis_divisor_tready  (Unsigned_divisor_tready),    // output wire s_axis_divisor_tready
    .s_axis_divisor_tdata   (divisor_tdata),              // input wire [31 : 0] s_axis_divisor_tdata
    .s_axis_dividend_tvalid (Unsigned_dividend_tvalid),   // input wire s_axis_dividend_tvalid
    .s_axis_dividend_tready (Unsigned_dividend_tready),   // output wire s_axis_dividend_tready
    .s_axis_dividend_tdata  (dividend_tdata),             // input wire [31 : 0] s_axis_dividend_tdata
    .m_axis_dout_tvalid     (Unsigned_div_finish),        // output wire m_axis_dout_tvalid
    .m_axis_dout_tdata      (Unsigned_dout_tdata)         // output wire [63 : 0] m_axis_dout_tdata
    );
`endif

// Multiplier
MultState signed_mult_state;
MultState signed_mult_nextstate;
MultState unsigned_mult_state;
MultState unsigned_mult_nextstate;
logic [31:0] mult_a_data;
logic [31:0] mult_b_data;
logic [63:0] signed_mult_out;
logic [63:0] unsigned_mult_out;
logic signed_mult_finish;
logic unsigned_mult_finish;


Signed_mult U_SignedMult (
    .CLK(clk),
    .SCLR(~reset),
    .A(mult_a_data),
    .B(mult_b_data),
    .P(signed_mult_out)
);

Unsigned_mult U_UnsignedMult (
    .CLK(clk),
    .SCLR(~reset),
    .A(mult_a_data),
    .B(mult_b_data),
    .P(unsigned_mult_out)
);

    always_ff @(posedge clk) begin
        if (reset == '0) begin
            mult_a_data <= '0;
            mult_b_data <= '0;
        end
        else if (EXE_ALUOp == `ALU_mul || EXE_ALUOp == `ALU_mulu) begin
            mult_a_data <= A;
            mult_b_data <= B;
        end
    end

    always_ff @(posedge clk) begin
        if (reset == '0) begin
            signed_mult_state <= MUL_IDLE;
            unsigned_mult_state <= MUL_IDLE;
        end
        else begin
            signed_mult_state <= signed_mult_nextstate;
            unsigned_mult_state <= unsigned_mult_nextstate;
        end
    end

    always_comb begin
        if(WB_ExceptionType!=`NoException)begin
            signed_mult_nextstate=MUL_IDLE;
        end else
            case (signed_mult_state)
            MUL_IDLE: begin
                if (EXE_ALUOp == `ALU_mul)
                    signed_mult_nextstate = MUL_WAIT1;
                else
                    signed_mult_nextstate = MUL_IDLE;
            end
            MUL_WAIT1: signed_mult_nextstate = MUL_WAIT2;
            MUL_WAIT2: signed_mult_nextstate = MUL_WAIT3;
            MUL_WAIT3: signed_mult_nextstate = MUL_WAIT4;
            MUL_WAIT4: signed_mult_nextstate = MUL_WAIT5;
            MUL_WAIT5: signed_mult_nextstate = MUL_FINISH;
            MUL_FINISH: signed_mult_nextstate = MUL_IDLE;
            default: signed_mult_nextstate = MUL_IDLE;
            endcase
    end

    always_comb begin
        if(WB_ExceptionType!=`NoException)begin
            unsigned_mult_nextstate=MUL_IDLE;
        end else
            case (unsigned_mult_state)
            MUL_IDLE: begin
                if (EXE_ALUOp == `ALU_mulu)
                    unsigned_mult_nextstate = MUL_WAIT1;
                else
                    unsigned_mult_nextstate = MUL_IDLE;
            end
            MUL_WAIT1: unsigned_mult_nextstate = MUL_WAIT2;
            MUL_WAIT2: unsigned_mult_nextstate = MUL_WAIT3;
            MUL_WAIT3: unsigned_mult_nextstate = MUL_WAIT4;
            MUL_WAIT4: unsigned_mult_nextstate = MUL_WAIT5;
            MUL_WAIT5: unsigned_mult_nextstate = MUL_FINISH;
            MUL_FINISH: unsigned_mult_nextstate = MUL_IDLE;
            default: unsigned_mult_nextstate = MUL_IDLE;
            endcase
    end

    assign signed_mult_finish = signed_mult_state == MUL_FINISH;
    assign unsigned_mult_finish = unsigned_mult_state == MUL_FINISH;
    assign div_finish   = Signed_div_finish | Unsigned_div_finish;                    //除法完成信号
    // assign mult_finish = ((EXE_ALUsel == `ALU2)&&(EXE_ALUOp == `ALU_mul || EXE_ALUOp == `ALU_mulu)) ? 1 : 0;     //乘法完成信号
    assign mult_finish = signed_mult_finish | unsigned_mult_finish;
    assign EXE_Finish   = mult_finish | div_finish;                                                //总完成信�?

    assign EXE_DatatoLO =   (signed_mult_finish) ? signed_mult_out[31:0] : 
                            (unsigned_mult_finish) ? unsigned_mult_out[31:0] :
                            (Signed_div_finish   ) ? Signed_dout_tdata[63:32]  : 
                            (Unsigned_div_finish ) ? Unsigned_dout_tdata[63:32]: 32'bx;

    assign EXE_DatatoHI =   (signed_mult_finish) ? signed_mult_out[63:32] : 
                            (unsigned_mult_finish) ? unsigned_mult_out[63:32] : 
                            (Signed_div_finish   ) ? Signed_dout_tdata[31:0]   : 
                            (Unsigned_div_finish ) ? Unsigned_dout_tdata[31:0] : 32'bx;

    assign EXE_ALU2_Stall = ((EXE_ALUOp == `ALU_div || EXE_ALUOp == `ALU_divu || EXE_ALUOp == `ALU_mul || EXE_ALUOp == `ALU_mulu) && EXE_Finish == '0) ? 1 : 0 ;
endmodule